<!DOCTYPE html>
<html>

<head>
  <script src="../konva.min.7-2-5.js"></script>
  <meta charset="utf-8" />
  <title>动物在海滩游戏 </title>
  <style>
    body {
      margin: 0;
      padding: 0;
      overflow: hidden;
      background-color: #f0f0f0;
    }
  </style>
</head>

<body>
  <div id="container"></div>
  <script>
    class AnimalGame {
      constructor(containerId) {
        this.width = window.innerWidth
        this.height = window.innerHeight
        this.containerId = containerId

        this.sources = {
          beach: 'beach.png',
          snake: 'snake.png',
          snake_glow: 'snake-glow.png',
          snake_black: 'snake-black.png',
          lion: 'lion.png',
          lion_glow: 'lion-glow.png',
          lion_black: 'lion-black.png',
          monkey: 'monkey.png',
          monkey_glow: 'monkey-glow.png',
          monkey_black: 'monkey-black.png',
          giraffe: 'giraffe.png',
          giraffe_glow: 'giraffe-glow.png',
          giraffe_black: 'giraffe-black.png',
        }

        this.isSpacePressed = false
      }

      /**
       * 加载图片
       */
      loadImages (sources, callback) {
        const assetDir = './assets/'
        const images = {}
        let loadedImages = 0
        const numImages = Object.keys(sources).length

        for (let src in sources) {
          images[src] = new Image()
          images[src].onload = () => {
            if (++loadedImages >= numImages) {
              callback(images)
            }
          }
          images[src].src = assetDir + sources[src]
        }
      }

      /**
       * 判断是否在矩形内
       */
      isNearOutline (animal, outline) {
        const ax = animal.x()
        const ay = animal.y()

        return ax > outline.x - 20 && ax < outline.x + 20 && ay > outline.y - 20 && ay < outline.y + 20
      }

      /**
       * 绘制背景
       */
      drawBackground (background, beachImg, text) {
        const context = background.getContext()
        context.drawImage(beachImg, 0, 0)
        context.setAttr('font', '20pt Calibri')
        context.setAttr('textAlign', 'center')
        context.setAttr('fillStyle', 'white')
        context.fillText(text, background.getStage().width() / 2, 40)
      }

      /**
       * 初始化舞台
       */
      initStage (images) {

        const stage = new Konva.Stage({
          container: this.containerId,
          width: 578,
          height: 530,
        })

        /**
         * 重写了Konva.Util.createCanvasElement方法，以确保所有创建的Canvas元素都包含willReadFrequently属性
         * warning
         * - Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true
         */
        Konva.Util.createCanvasElement = () => {
          const canvas = document.createElement('canvas')
          canvas.getContext('2d', { willReadFrequently: true })
          return canvas
        }


        const background = new Konva.Layer()
        const animalLayer = new Konva.Layer()
        const animalShapes = []
        let score = 0


        // 定义动物位置
        const animals = {
          snake: { x: 10, y: 70 },
          giraffe: { x: 90, y: 70 },
          monkey: { x: 275, y: 70 },
          lion: { x: 400, y: 70 },
        }

        const outlines = {
          snake_black: { x: 275, y: 350 },
          giraffe_black: { x: 390, y: 250 },
          monkey_black: { x: 300, y: 420 },
          lion_black: { x: 100, y: 390 },
        }

        // 创建动物集合
        for (let key in animals) {
          const anim = animals[key]
          const animal = new Konva.Image({
            image: images[key],
            x: anim.x,
            y: anim.y,
            draggable: true,
          })

          animal.on('dragstart', () => {
            animal.moveToTop()
          })

          animal.on('dragend', () => {
            const outline = outlines[key + '_black']
            if (!animal.inRightPlace && this.isNearOutline(animal, outline)) {
              animal.position({ x: outline.x, y: outline.y })
              animal.inRightPlace = true

              if (++score >= 4) {
                const text = 'You win! Enjoy your booty!'
                this.drawBackground(background, images.beach, text)
              }

              // 延迟禁用拖动功能，以确保动物 拖拽结束
              setTimeout(() => {
                animal.draggable(false)
              }, 50)
            }
          })

          // 鼠标悬停时高亮动物
          animal.on('mouseover', () => {
            animal.image(images[key + '_glow'])
            document.body.style.cursor = 'pointer'
          })

          // 鼠标离开时恢复原始图片
          animal.on('mouseout', () => {
            animal.image(images[key])
            document.body.style.cursor = 'default'
          })

          animal.on('dragmove', () => {
            document.body.style.cursor = 'pointer'
          })

          animalLayer.add(animal)
          animalShapes.push(animal)
        }

        // 创建动物的黑色边框
        for (let key in outlines) {
          const imageObj = images[key]
          const out = outlines[key]

          const outline = new Konva.Image({
            image: imageObj,
            x: out.x,
            y: out.y,
          })

          animalLayer.add(outline)
        }

        stage.add(background)
        stage.add(animalLayer)

        this.drawBackground(background, images.beach, '动物集合!')

        // 监听鼠标滚轮事件
        stage.on('wheel', (e) => {
          e.evt.preventDefault()

          const oldScale = stage.scaleX()
          const pointer = stage.getPointerPosition()

          const mousePointTo = {
            x: (pointer.x - stage.x()) / oldScale,
            y: (pointer.y - stage.y()) / oldScale,
          }

          const newScale = e.evt.deltaY > 0 ? oldScale * 1.1 : oldScale / 1.1

          stage.scale({ x: newScale, y: newScale })

          const newPos = {
            x: pointer.x - mousePointTo.x * newScale,
            y: pointer.y - mousePointTo.y * newScale,
          }

          stage.position(newPos)
          stage.batchDraw()

          this.drawBackground(background, images.beach, 'Ahoy! Put the animals on the beach!')
        })

        // 添加监听键盘事件
        window.addEventListener('keydown', (e) => {
          if (e.code === 'Space') {
            this.isSpacePressed = true
            stage.draggable(true)
            document.body.style.cursor = 'grab'
          }
        })

        window.addEventListener('keyup', (e) => {
          if (e.code === 'Space') {
            this.isSpacePressed = false
            stage.draggable(false)
            document.body.style.cursor = 'default'
          }
        })

        // 添加监听拖拽事件
        stage.on('dragstart', () => {
          if (this.isSpacePressed) {
            document.body.style.cursor = 'grabbing'
          }
        })

        stage.on('dragend', () => {
          if (this.isSpacePressed) {
            document.body.style.cursor = 'grab'
          }
        })
      }

      /**
       * 开始游戏
       */
      start () {
        this.loadImages(this.sources, images => this.initStage(images))
      }
    }

    const game = new AnimalGame('container')
    game.start();

  </script>
</body>

</html>